package model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.Semaphore;

import controller.Controller;
import view.View;

public class Table extends Thread {
	private ArrayList<Player> players = new ArrayList<Player>();
	private ArrayList<Card> cards = new ArrayList<Card>();
	private ArrayList<Card> calledCards = new ArrayList<Card>();
	private Card trumpCard;
	private Colors trumpColor;
	private Controller controller;
	private int cardsPerPlayer = 2;
	private int dealer;
	private boolean moreCards = true;
	private View view;
	private volatile boolean endOfGame = false;
	private int remainingRounds = 0;
	
	public Table(int numberOfBots, String humanPlayerName, View view, Controller controller, Semaphore semaphore) {
		this.view = view;
		this.controller = controller;
		Player humanPlayer = new HumanPlayer(0,humanPlayerName,semaphore,controller);
		controller.setPlayer((HumanPlayer)humanPlayer);
		players.add(humanPlayer);
		view.createPlayerView(humanPlayer, numberOfBots+1);
		dealer = new Random().nextInt(numberOfBots+1);
		for(int i = 0; i < numberOfBots; ++i) {
			Player bot = new Bot(i+1,i+1 + ". bot",numberOfBots+1);
			players.add(bot);
			view.createPlayerView(bot, numberOfBots+1);
		}
		for(int i = 2; i < 15; ++i) {
			Card temp = new Card(Colors.Diamond,i);
			cards.add(temp);
			view.createCardView(temp);
			temp = new Card(Colors.Club,i);
			cards.add(temp);
			view.createCardView(temp);
			temp = new Card(Colors.Heart,i);
			cards.add(temp);
			view.createCardView(temp);
			temp = new Card(Colors.Spade,i);
			cards.add(temp);
			view.createCardView(temp);
		}
		remainingRounds = ((52/players.size())-2)*2+1;
	}
	
	public int getCardsPerPlayer() {
		return cardsPerPlayer;
	}
	
	public Card getTrumpCard() {
		return trumpCard;
	}
	
	private void startARound() {
		
		view.update();
		if (moreCards) {
			trumpCard = null;
			deal();
			playARound();
			++cardsPerPlayer;
			if ((cardsPerPlayer * players.size()) > 52 ) {
				moreCards = false;
				--cardsPerPlayer;
			}
		}
		else if (!moreCards && cardsPerPlayer == 2) {
			endOfGame=true;
			
			int winnerID=0;
			int winnerPoint=players.get(0).getPoints();
			view.update();
			for(int i = 0; i < players.size(); i++) {
				if (players.get(i).getPoints() > winnerPoint) {
					winnerID = i;
					winnerPoint = players.get(winnerID).getPoints();
				}
			}
			
			controller.endGame(players.get(winnerID).getName());
		}
		else {
			--cardsPerPlayer;
			trumpCard = null;
			deal();
			playARound();
		}
		
		for(int i = 0; i < players.size(); i++) {
			players.get(i).processRoundData();
		}
		
		if (!endOfGame)
			--remainingRounds;
	}

	private void playARound() {
		int player = (dealer+1) % (players.size());
		int nextPlayer = dealer;
		players.get(player).setPlayerTurn(true,false);
		nextPlayer = (player-1) % players.size();
		if (nextPlayer < 0)
			nextPlayer = players.size()-1;
		players.get(nextPlayer).setPlayerTurn(false, true);
		for(int i = 0; i < players.size(); ++i) {
			int bid = players.get(i).askForABid(players.size(),trumpColor);
			while ((bid < 0 ) || (bid > cardsPerPlayer)) {
				bid = players.get(i).askForABid(players.size(),trumpColor);
			}
			players.get(i).setBid(bid);
		}
		
		view.update();
	
		for(int i = 0; i < cardsPerPlayer; ++i) {
			Player actualPlayer = players.get(player);
			int bestPlayer = player;
			Card bestCard = null;
			
			actualPlayer.setPlayerTurn(true,false);
			nextPlayer = (player-1) % players.size();
			if (nextPlayer < 0)
				nextPlayer = players.size()-1;
			players.get(nextPlayer).setPlayerTurn(false, true);
			bestCard = actualPlayer.askForACard(calledCards, 0, trumpColor);
			actualPlayer.removeCard(bestCard);
			actualPlayer.setPlayerTurn(false,false);
			nextPlayer = (player-1) % players.size();
			if (nextPlayer < 0)
				nextPlayer = players.size()-1;
			players.get(nextPlayer).setPlayerTurn(false, false);
			calledCards.add(bestCard);
			for(int j = 0; j < players.size(); ++j)
				players.get(j).addToAllCalledCards(bestCard);
			view.update();
			for(int j = 1; j < players.size(); ++j) {
				if (player == 0)
					player = players.size();
				player = player-1;
				
				Card nextPlayerCard = null;
				actualPlayer = players.get(player);
				actualPlayer.setPlayerTurn(true,false);
				nextPlayer = (player-1) % players.size();
				if (nextPlayer < 0)
					nextPlayer = players.size()-1;
				if (j == (players.size()-1))
					players.get(nextPlayer).setPlayerTurn(false, false);
				else
					players.get(nextPlayer).setPlayerTurn(false, true);
				if (actualPlayer.hasColor(calledCards.get(0).getColor())) {
					nextPlayerCard = actualPlayer.askForACard(calledCards, j, trumpColor);
					while ((nextPlayerCard.getColor() != calledCards.get(0).getColor()) && (nextPlayerCard.getColor() != trumpColor)) {
						nextPlayerCard = actualPlayer.askForACard(calledCards, j, trumpColor);
					}
				}
				else
					nextPlayerCard = actualPlayer.askForACard(calledCards, j, trumpColor);

				actualPlayer.removeCard(nextPlayerCard);
				calledCards.add(nextPlayerCard);
				for(int k = 0; k < players.size(); ++k)
					players.get(k).addToAllCalledCards(nextPlayerCard);
				actualPlayer.setPlayerTurn(false,false);
				nextPlayer = (player-1) % players.size();
				if (nextPlayer < 0)
					nextPlayer = players.size()-1;
				players.get(nextPlayer).setPlayerTurn(false, false);
				view.update();
				if (!bestCard.isBetterThan(nextPlayerCard,calledCards.get(0).getColor(),trumpColor)) {
					bestCard = nextPlayerCard;
					bestPlayer = player;
				}
			}
			player = bestPlayer;
			players.get(bestPlayer).addWin();
			
			try {
				Thread.sleep(1500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			for(int k = 0; k < calledCards.size(); ++k)
				calledCards.get(k).setPlayer(-2);
			calledCards.removeAll(calledCards);
			view.update();
		}
	}
	
	private void deal() {
		if ((cardsPerPlayer*players.size()) == 52) {
			Collections.shuffle(cards);
			trumpCard = cards.get(0);
			trumpCard.setPlayer(-3);
			trumpColor = trumpCard.getColor();
		}
		Collections.shuffle(cards);
		int k = 0;
		for(int i=0; i < cardsPerPlayer; ++i) {
			for(int j=dealer; j < (players.size()+dealer); ++j) {
				cards.get(k).setPlayer(j % players.size());
				players.get(j % players.size()).receiveACard(cards.get(k++));
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				view.update();
			}
		}
		if ((cardsPerPlayer*players.size()) < 52) {
			trumpCard = cards.get(k);
			trumpCard.setPlayer(-3);
			trumpColor = trumpCard.getColor();
			for(int j = 0; j < players.size(); ++j)
				players.get(j).addToAllCalledCards(trumpCard);
		}
		dealer = (dealer+1) % players.size();
		((HumanPlayer)players.get(0)).sortCards(trumpColor);
	}

	public void run() {
		while (!endOfGame)
			startARound();
	}

	public ArrayList<Card> getCalledCards() {
		return calledCards;
	}

	public ArrayList<Player> getPlayers() {
		return players;
	}
	
	public int getRemainingRounds() {
		return remainingRounds;
	}

	public void stopGame() {
		endOfGame = true;
	}
}